#pragma once

#pragma unmanaged

#include <set>

#pragma managed

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;

namespace BroodWar
{
	void Log(String^ string);

	template <typename TNative, typename TManaged> //TNative is inherited from BWAPI::Type
	private ref class EnumMap
	{
	private:
		Dictionary<String^, TManaged>^ dictionaryForward;
		Dictionary<TManaged, String^>^ dictionaryBackward;
		TNative (*stringToTypeConverter)(std::string);
		char* (*typeToStringConverter)(TNative);

	internal:
		void Initialize(
			int size,
			std::set<TNative> natives,
			TNative (*_stringToTypeConverter)(std::string),
			char* (*_typeToStringConverter)(TNative))
		{
			dictionaryForward = gcnew Dictionary<String^, TManaged>(size);
			dictionaryBackward = gcnew Dictionary<TManaged, String^>(size);
			stringToTypeConverter = _stringToTypeConverter;
			typeToStringConverter = _typeToStringConverter;

			std::set<TNative>::iterator it;
			for(it = natives.begin(); it != natives.end(); it++)
			{
				String^ name = gcnew String((*typeToStringConverter)(*it));
				TManaged managed;
				String^ tempName = name
					->Replace('-', '_')
					->Replace(' ', '_')
					->Replace("(", String::Empty)
					->Replace(")", String::Empty);
				if (!System::Enum::TryParse<TManaged>(tempName, true, managed) &&
					!System::Enum::TryParse<TManaged>(tempName->Replace("_", String::Empty), true, managed))
				{
					Log(System::String::Format("Cannot parse {0} nor {1} of type {2}", tempName, tempName->Replace("_", String::Empty), TManaged::typeid));
				}
				else
				{
					dictionaryForward->Add(name, managed);
					dictionaryBackward->Add(managed, name);
				}
			}
		}

		EnumMap(
			int size,
			const std::set<TNative> &(*membersEnumerator)(),
			TNative (*_stringToTypeConverter)(std::string),
			char* (*_typeToStringConverter)(TNative))
		{
			std::set<TNative> natives = (*membersEnumerator)();
			Initialize(size, natives, _stringToTypeConverter, _typeToStringConverter);
		}

		EnumMap(
			int size,
			std::set<TNative> &(*membersEnumerator)(),
			TNative (*_stringToTypeConverter)(std::string),
			char* (*_typeToStringConverter)(TNative))
		{
			std::set<TNative> natives = (*membersEnumerator)();
			Initialize(size, natives, _stringToTypeConverter, _typeToStringConverter);
		}

		TManaged Managed(TNative native)
		{
			return dictionaryForward[gcnew String(typeToStringConverter(native))];
		}

		TManaged Managed(const TNative *native)
		{
			return dictionaryForward[gcnew String(typeToStringConverter(*native))];
		}

		TNative* NativePointer(TManaged managed)
		{
			return new TNative(Native(managed));
		}

		TNative Native(TManaged managed)
		{
			IntPtr str = Marshal::StringToHGlobalAnsi(dictionaryBackward[managed]);
			std::string stdstr = (char*)str.ToPointer();
			TNative rez = (*stringToTypeConverter)(stdstr);
			Marshal::FreeHGlobal(str);
			return rez;
		}
	};
}
